package com.linkhub.oms.admin.modules.product.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.google.common.collect.Lists;
import com.linkhub.oms.admin.modules.product.domain.vo.LocalProductExcelVO;
import com.linkhub.oms.admin.modules.product.excel.dto.LocalProductExcelDataDTO;
import com.linkhub.oms.admin.modules.product.excel.dto.PackagingMaterialExcelDataDTO;
import com.linkhub.oms.admin.modules.product.excel.listener.LocalProductExcelDataListener;
import com.linkhub.oms.admin.modules.product.excel.listener.PackagingMaterialExcelDataListener;
import com.linkhub.oms.admin.modules.product.service.LocalProductExcelService;
import com.linkhub.oms.common.enums.mp.LocalProductTypeEnum;
import com.linkhub.oms.common.util.QtStreamUtil;
import com.linkhub.oms.common.util.WmsUtil;
import com.linkhub.oms.component.excel.util.EasyExcelUtil;
import com.linkhub.oms.dao.mpservice.ILocalProductService;
import com.linkhub.oms.dao.mpservice.ILocalProductSysBarCodeService;
import com.linkhub.oms.entity.LocalProductEntity;
import com.linkhub.oms.entity.LocalProductSysBarCodeEntity;
import com.opencsv.CSVWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

import static com.linkhub.oms.common.constant.FileConstant.EXPORT_MATERIAL_FILE_NAME;

/**
 * @Description: LocalProductExcelService
 * @Author: echo
 * @Date: 2024/5/12 13:11
 * @Version: 1.0
 */
@Slf4j
@Service
public class LocalProductExcelServiceImpl implements LocalProductExcelService {

    @Resource
    private ILocalProductService localProductService;
    @Resource
    private ILocalProductSysBarCodeService localProductSysBarCodeService;

    public static final List<SFunction<LocalProductEntity, String>> LOCAL_PRODUCT_CODE_COLUMNS =
            Lists.newArrayList(LocalProductEntity::getSku,
                    LocalProductEntity::getUpc,
                    LocalProductEntity::getFnsku,
                    LocalProductEntity::getOtherCode);

    public static final List<Function<LocalProductExcelDataDTO, String>> LOCAL_PRODUCT_EXCEL_CODE_COLUMNS =
            Lists.newArrayList(LocalProductExcelDataDTO::getSku,
                    LocalProductExcelDataDTO::getUpc,
                    LocalProductExcelDataDTO::getFnsku,
                    LocalProductExcelDataDTO::getOtherCode);

    public static final List<Function<PackagingMaterialExcelDataDTO, String>> PACKAGING_MATERIAL_EXCEL_CODE_COLUMNS =
            Lists.newArrayList(PackagingMaterialExcelDataDTO::getSku,
                    PackagingMaterialExcelDataDTO::getUpc,
                    PackagingMaterialExcelDataDTO::getFnsku,
                    PackagingMaterialExcelDataDTO::getOtherCode);


    @Override
    public LocalProductExcelVO importExcel(String userId, Integer productType, MultipartFile file) {
        return Objects.equals(LocalProductTypeEnum.SINGLE_PRODUCT.getType(), productType)
                ? importLocalProducts(userId, file)
                : importPackagingMaterials(userId, file);
    }

    private LocalProductExcelVO importLocalProducts(String userId, MultipartFile file) {
        LocalProductExcelDataListener excelDataListener = new LocalProductExcelDataListener();
        List<LocalProductExcelDataDTO> list = EasyExcelUtil.read(file, LocalProductExcelDataDTO.class, excelDataListener);
        int failTotal = CollUtil.size(excelDataListener.acquireInValidList());
        if (CollUtil.isEmpty(list)) {
            return LocalProductExcelVO.failResult(failTotal);
        }

        // 去重
        List<LocalProductExcelDataDTO> pendingSaveList = localProductRepeatFilter(userId,
                list,
                LOCAL_PRODUCT_CODE_COLUMNS,
                LOCAL_PRODUCT_EXCEL_CODE_COLUMNS);

        int dataSize = CollUtil.size(list);
        int saveSize = CollUtil.size(pendingSaveList);
        int repeatSize = dataSize - saveSize;

        // convert
        List<LocalProductEntity> products = QtStreamUtil.map(pendingSaveList, dto -> convert(userId, dto));
        localProductService.saveBatch(products);
        return LocalProductExcelVO.successResult(failTotal + repeatSize, saveSize);
    }


    private LocalProductExcelVO importPackagingMaterials(String userId, MultipartFile file) {
        PackagingMaterialExcelDataListener excelDataListener = new PackagingMaterialExcelDataListener();
        List<PackagingMaterialExcelDataDTO> list = EasyExcelUtil.read(file, PackagingMaterialExcelDataDTO.class, excelDataListener);
        int failTotal = CollUtil.size(excelDataListener.acquireInValidList());
        if (CollUtil.isEmpty(list)) {
            return LocalProductExcelVO.failResult(failTotal);
        }

        // 去重
        List<PackagingMaterialExcelDataDTO> pendingSaveList = localProductRepeatFilter(userId,
                list,
                LOCAL_PRODUCT_CODE_COLUMNS,
                PACKAGING_MATERIAL_EXCEL_CODE_COLUMNS);

        int dataSize = CollUtil.size(list);
        int saveSize = CollUtil.size(pendingSaveList);
        int repeatSize = dataSize - saveSize;

        // convert
        List<LocalProductEntity> products = QtStreamUtil.map(pendingSaveList, dto -> convert(userId, dto));
        localProductService.saveBatch(products);
        return LocalProductExcelVO.successResult(failTotal + repeatSize, saveSize);
    }

    public void exportPackagingMaterialsToCSV(HttpServletResponse response) {
        String fileName;
        try {
            fileName = URLEncoder.encode(EXPORT_MATERIAL_FILE_NAME, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"; filename*=utf-8''" + fileName);

        response.setContentType("text/csv");
        response.setCharacterEncoding("UTF-8");

        // 使用 StringWriter 缓存内容
        try (StringWriter stringWriter = new StringWriter();
             CSVWriter csvWriter = new CSVWriter(stringWriter)) {

            // 添加 CSV 头
            String[] csvHeader = {"货主", "代码", "条码", "名称", "英文名称", "长", "宽", "高", "体积", "自重", "半径", "材质", "形状", "自定义1", "自定义2", "自定义3", "自定义4", "更新时间"};
            csvWriter.writeNext(csvHeader);

            List<LocalProductEntity> localProductEntities = localProductService.getLocalProductByType(LocalProductTypeEnum.PACKAGING_MATERIAL);

            // 写入 CSV 数据行
            for (LocalProductEntity localProduct : localProductEntities) {
                final String specificationProductInfo = localProduct.getSpecificationProductInfo();
                String[] parts = specificationProductInfo != null ? specificationProductInfo.split("-") : new String[0];
                String[] result = Arrays.copyOf(parts, 4);

                for (int i = 0; i < result.length; i++) {
                    if (result[i] == null || result[i].isEmpty()) {
                        result[i] = "";
                    }
                }

                String[] data = {
                        "", // 货主
                        "=\"" + WmsUtil.createItemCode(localProduct.getUserId(), localProduct.getSkuId()) + "\"", // 代码
                        "=\"" + WmsUtil.createBarCode(localProduct.getSysBarCode(), localProduct.getUpc(), localProduct.getFnsku(), localProduct.getOtherCode()) + "\"", // 条码
                        "=\"" + localProduct.getProductCTitle() + "\"", // 名称
                        "=\"" + localProduct.getProductEnTitle() + "\"", // 英文名称
                        "=\"" + result[0] + "\"", // 长
                        "=\"" + result[1] + "\"", // 宽
                        "=\"" + result[2] + "\"", // 高
                        "", // 体积
                        "=\"" + result[3] + "\"", // 自重
                        "", // 半径
                        "", // 材质
                        "", // 形状
                        "=\"" + localProduct.getUserId() + "\"", // 自定义1
                        "", // 自定义2
                        "", // 自定义3
                        "", // 自定义4
                        "=\"" + String.valueOf(localProduct.getUpdateTime()) + "\"" // 更新时间
                };
                csvWriter.writeNext(data);
            }

            // 所有数据成功写入到 StringWriter 后再输出到 HttpServletResponse
            try (PrintWriter writer = response.getWriter()) {
                writer.write(stringWriter.toString());
            }

        } catch (Exception e) {
            throw new RuntimeException("导出CSV文件失败", e);
        }
    }


    private LocalProductEntity convert(String userId, LocalProductExcelDataDTO localProductExcelData) {
        LocalProductEntity entity = new LocalProductEntity();
        BeanUtil.copyProperties(localProductExcelData,
                entity,
                CopyOptions.create().ignoreError().ignoreNullValue());
        entity.setUserId(userId);
        entity.setProductType(LocalProductTypeEnum.SINGLE_PRODUCT.getType());
        entity.setSkuId(IdUtil.getSnowflake().nextIdStr());
        entity.setCategoryCode(String.valueOf(localProductExcelData.getCategoryCode()));
        entity.setSysBarCode(getSysBarCode(userId, entity.getSkuId()));
        entity.setCreatedTime(LocalDateTime.now());
        entity.setUpdateTime(LocalDateTime.now());

        return entity;

    }

    private LocalProductEntity convert(String userId, PackagingMaterialExcelDataDTO packagingMaterial) {
        LocalProductEntity entity = new LocalProductEntity();
        BeanUtil.copyProperties(packagingMaterial,
                entity,
                CopyOptions.create().ignoreError().ignoreNullValue());
        entity.setUserId(userId);
        entity.setProductType(LocalProductTypeEnum.PACKAGING_MATERIAL.getType());
        entity.setSkuId(IdUtil.getSnowflake().nextIdStr());
        entity.setSysBarCode(getSysBarCode(userId, entity.getSkuId()));
        entity.setCreatedTime(LocalDateTime.now());
        entity.setUpdateTime(LocalDateTime.now());

        return entity;

    }

    private String getSysBarCode(String userId, String skuId) {
        return String.format("BC%010d", getBarCode(userId, skuId));
    }

    private Long getBarCode(String userId, String skuId) {
        LocalProductSysBarCodeEntity entity = new LocalProductSysBarCodeEntity();
        entity.setUserId(userId);
        entity.setSkuId(skuId);
        localProductSysBarCodeService.save(entity);
        return entity.getId();
    }

    private <T> List<T> localProductRepeatFilter(String userId,
                                                 List<T> dataList,
                                                 List<SFunction<LocalProductEntity, String>> entityColumns,
                                                 List<Function<T, String>> dataProperties) {

        List<T> resultList = dataList;
        for (int i = 0; i < entityColumns.size(); i++) {
            if (CollUtil.isEmpty(resultList)) {
                break;
            }
            SFunction<LocalProductEntity, String> column = entityColumns.get(i);
            Function<T, String> property = dataProperties.get(i);
            List<String> dataCodes = QtStreamUtil.filterAndMap(dataList,
                    product -> StringUtils.isNotBlank(property.apply(product)),
                    property);
            List<String> codes = localProductService.listLocalProductCodes(userId, dataCodes, column);
            resultList = repeatFilter(codes, resultList, property);
        }

        return resultList;

    }

    private <T> List<T> repeatFilter(List<String> codes,
                                     List<T> dataList,
                                     Function<T, String> dataProperty) {

        if (CollUtil.isEmpty(codes)) {
            return dataList;
        }
        return QtStreamUtil.filter(dataList, data -> !codes.contains(dataProperty.apply(data)));
    }
}
